package controller

import (
	"coursedesign/projectGo/common"
	"coursedesign/projectGo/model"
	"coursedesign/projectGo/response"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"strconv"
)

type IDeliverController interface {
	RestController
	Visual(c *gin.Context)
}

type DeliverController struct {
	DB *gorm.DB
}

func NewDeliverController() IDeliverController {
	db := common.GetDB()
	return DeliverController{DB: db}
}

func (d DeliverController) Create(c *gin.Context) {
	// 前端传：货物名、订单号、提单号、业务号、日期、出库件数、吨数
	var re model.DeliverAPI
	c.ShouldBind(&re)
	// 判断吨数是否正常
	if re.Quality<=0 || re.Quantity<=0 {
		response.Fail(c, "货物件数/吨数不可为0", nil)
		return
	}
	fmt.Println(re)
	// 查询公司是否存在
	if d.DB.Model(&model.Company{}).Where("id=?", re.Cid).
		Find(&model.Company{}).RowsAffected == 0 {
		response.Fail(c, "查无此公司", nil)
		return
	}
	// 判断是否已存在
	sqlWords := "goodid=? and bussinessid=? and ladeid=? and ddate=?"
	result := d.DB.Model(&model.Deliver{}).Where(sqlWords, re.Goodid, re.Bussinessid,
		re.Ladeid, re.Ddate).Find(&model.Deliver{}).RowsAffected
	if result != 0 {
		response.Fail(c, "货物已出库", nil)
		return
	}
	// 查询入库表（查询货物表货物名），没有return，有就拿到足够吨数
	// 根据货物名查询货物表, 并按入库时间排序
	var enter []model.Enter
	if d.DB.Model(&model.Enter{}).Order("edate ASC").Where("name=? and cid=?", re.Name, re.Cid).
		Find(&enter).RowsAffected == 0 {
		response.Fail(c, "此公司查无此类货物", nil)
		return
	}
	// 计算库存是否够出库
	cal := re.Quality
	i := 0
	for ; i < len(enter); i++ {
		// 计算吨数和件数是否匹配
		qua := re.Quality
		quan := re.Quantity
		weight := qua / float64(quan)
		trueWeight := enter[i].Quality / float64(enter[i].Quantity)
		if weight != trueWeight {
			str := fmt.Sprintf("出库件数与吨数不匹配，应为 %.2f 吨/件",trueWeight)
			response.Fail(c, str, nil)
			return
		}

		cal = cal - enter[i].RemainQuality
		if cal <= 0 {
			break
		}
	}
	if cal > 0 {
		response.Fail(c, "库存不足，无法出库", nil)
		return
	}
	var fee float64
	// 赋初值
	writeQuality := re.Quality
	// 累计堆存费
	enterU := make([]string, 0) // 保存入库uuid
	for j := 0; j <= i; j++ {
		if enter[j].RemainQuality == 0 {
			continue
		}
		if enter[j].RemainQuality < writeQuality { // 当前订单无法满足需求
			writeQuality = writeQuality - enter[j].RemainQuality
			enterU = append(enterU, enter[j].UUID.String())

			// 计算堆存费
			subHour := re.Ddate.Sub(enter[j].Edate).Hours()
			subDays := subHour/24
			fmt.Println(subHour, subDays)
			fmt.Println(subHour, subDays)
			if subDays >= 14 {
				fee += (subDays - 13) * enter[j].RemainQuality * 0.5
				fmt.Println(fee)
			}
			// 修改入库货物剩余吨数
			d.DB.Model(&enter[j]).Update("remain_quality", 0)
		} else { // 恰好满足 或 当前订单货物比需求多
			enterU = append(enterU, enter[j].UUID.String())
			// 修改入库货物剩余吨数
			d.DB.Model(&enter[j]).Update("remain_quality", enter[j].RemainQuality-writeQuality)
			// 计算堆存费
			subHours := re.Ddate.Sub(enter[j].Edate).Hours()
			subDays := subHours/24
			if subDays >= 14 {
				fee += (subDays - 13) * writeQuality * 0.5
			}
			break
		}
	}
	// 转化数组到数据库中数组类型
	// 格式化堆存费为小数点后2位
	fee, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", fee), 64)
	// 创建出库结构体
	createDeliver := model.Deliver{
		// 除了堆存费都不变
		UUID:        uuid.New(),
		Name:        re.Name,
		Cid:         re.Cid,
		Enterid:     enterU,
		Ddate:       re.Ddate,
		Goodid:      re.Goodid,
		Ladeid:      re.Ladeid,
		Bussinessid: re.Bussinessid,
		Quantity:    re.Quantity,
		Quality:     re.Quality,
		Fee:         fee,
	}
	err := d.DB.Model(&createDeliver).Create(&createDeliver).Error
	if err != nil {
		response.Fail(c, err.Error(), nil)
		return
	}
	response.Success(c, gin.H{"deliver": createDeliver}, "创建成功")
}

func (d DeliverController) Update(c *gin.Context) {
	// 前端传来uuid、订单号、提单号、业务号、日期、件数、吨数、名称
	// 绑定body中参数（更新后的值）
	var re model.DeliverAPI
	c.ShouldBind(&re)
	// 需要大于一吨，go计算不准确
	if re.Quality < 1 {
		response.Fail(c, "请输入大于 1吨 的值", nil)
		return
	}
	// 查询公司是否存在
	if d.DB.Model(&model.Company{}).Where("id=?", re.Cid).
		Find(&model.Company{}).RowsAffected == 0 {
		response.Fail(c, "查无此公司", nil)
		return
	}
	// 判断货物存在
	if d.DB.Model(&model.Enter{}).Where("name=? and cid=?", re.Name, re.Cid).
		Find(&model.Enter{}).RowsAffected == 0 {
		response.Fail(c, "此公司查无此类货物", nil)
		return
	}
	// 判断是否已存在
	u := c.Param("uuid")
	uid, _ := uuid.Parse(u)
	// 更新后出库信息
	update := model.Deliver{
		UUID:        uid,
		Name:        re.Name,
		Cid:         re.Cid,
		Goodid:      re.Goodid,
		Ladeid:      re.Ladeid,
		Bussinessid: re.Bussinessid,
		Ddate:       re.Ddate,
		Quantity:    re.Quantity,
		Quality:     re.Quality,
	}
	if update.Quantity <= 0 || update.Quality <= 0 {
		response.Fail(c, "货物件数/吨数不可为0", nil)
		return
	}

	// 拿到更新前数据
	var beforeDeliver model.Deliver
	err := d.DB.Where("uuid=?", uid).First(&beforeDeliver).Error
	if err != nil {
		response.Fail(c, err.Error(), nil)
		return
	}
	qua := re.Quality
	quan := re.Quantity
	weight := qua / float64(quan)
	trueWeight := beforeDeliver.Quality / float64(beforeDeliver.Quantity)
	fmt.Println(weight, trueWeight)
	fmt.Println(quan ,qua)
	if weight != trueWeight {
		str := fmt.Sprintf("出库件数与吨数不匹配，应为 %.2f 吨/件",trueWeight)
		response.Fail(c, str, nil)
		return
	}
	// 出库吨数变->入库剩余吨数变
	enterU := make([]uuid.UUID, 0) // 保存入库uuid
	// 判断出的少了还是多了
	// 获取相应入库信息
	enterUUID := beforeDeliver.Enterid
	// 入库uuid切片
	for i := 0; i < len(enterUUID); i++ {
		en, _ := uuid.Parse(enterUUID[i])
		enterU = append(enterU, en)
	}
	cal := update.Quality - beforeDeliver.Quality
	if cal > 0 {
		// 查询入库表，没有return，有就拿到足够吨数
		// 根据货物名查询入库表 并 按入库时间升序
		var getEnter []model.Enter
		if d.DB.Model(&model.Enter{}).Order("edate ASC").Where("name=? and cid=?",
			update.Name, update.Cid).Find(&getEnter).RowsAffected == 0 {
			response.Fail(c, "此仓库查无此类货物", nil)
			return
		}
		// 计算库存是否够出库
		i := 0
		prediction := cal
		for ; i < len(getEnter); i++ {
			prediction = prediction - getEnter[i].RemainQuality
			if prediction <= 0 {
				break
			}
		}
		if prediction > 0 {
			response.Fail(c, "库存不足，无法出库", nil)
			return
		}
		// 赋初值：更新后的吨数
		writeQuality := update.Quality - beforeDeliver.Quality

		// 逐条写入
		for j := 0; j <= i; j++ {
			if getEnter[j].RemainQuality == 0 { // 不从剩余吨数为零的货里取货
				continue
			}
			if getEnter[j].RemainQuality < writeQuality { // 当前订单无法满足需求
				writeQuality = writeQuality - getEnter[j].RemainQuality
				// 记录出库的货物批次
				flag := 0
				for k:=0; k<len(enterU); k++ {
					if enterU[k] ==getEnter[j].UUID {
						flag = 1
						break
					}
				}
				if flag == 0 {
					enterU = append(enterU, getEnter[j].UUID)
				}
				// 修改入库货物剩余吨数
				d.DB.Model(&model.Enter{}).Where("uuid=?", getEnter[j].UUID).
					Update("remain_quality", 0)
			} else { // 恰好满足 或 当前订单货物比需求多
				flag := 0
				for k:=0; k<len(enterU); k++ {
					if enterU[k] ==getEnter[j].UUID {
						flag = 1
						break
					}
				}
				if flag == 0 {
					enterU = append(enterU, getEnter[j].UUID)
				}
				// 修改入库货物剩余吨数
				d.DB.Model(&model.Enter{}).Where("uuid=?", getEnter[j].UUID).
					Update("remain_quality", getEnter[j].RemainQuality-writeQuality)
				break
			}
		}
	}
	/*
	else if cal < 0 { // 变少了
		// 改少了，就放回原来那批货
		var returnEnter model.Enter
		index := len(beforeDeliver.Enterid) - 1
		subDeliver := cal
		for i := index; i >= 0; i-- {
			d.DB.Model(&model.Enter{}).Where("uuid=?", beforeDeliver.Enterid[i]).
				First(&returnEnter)
			sub := returnEnter.Quality - returnEnter.RemainQuality
			if sub > 0 {
				if sub-subDeliver >= 0 {
					if sub-subDeliver==0 {
						flag := 0
						uid, _ := uuid.Parse(beforeDeliver.Enterid[i])
						for j:=0; j<len(enterU); j++ {
							if enterU[j] == uid {
								flag = 1
								break
							}
						}
						if flag == 1 {

						}
					}

					d.DB.Model(&model.Enter{}).Where("uuid=?", beforeDeliver.Enterid[i]).
						UpdateColumn("remain_quality", returnEnter.RemainQuality+subDeliver)
					return
	}

				d.DB.Model(&model.Enter{}).Where("uuid=?", beforeDeliver.Enterid[i]).
					UpdateColumn("remain_quality", sub)
				subDeliver -= sub
			}
		}
	}
	 */
	// 根据uuid查找入库
	var enter []model.Enter
	d.DB.Model(&model.Enter{}).Find(&enter, enterU)
	// 重新计算堆存费
	for i := 0; i < len(enter); i++ {
		subHours := update.Ddate.Sub(enter[i].Edate).Hours()
		subDays := subHours/24
		if subDays >= 14 {
			update.Fee += (subDays - 13) * update.Quality * 0.5
		}
	}
	// 修改
	err = d.DB.Model(&update).Where(&beforeDeliver).Updates(&update).Error
	if err != nil {
		response.Fail(c, err.Error(), nil)
		return
	}
	response.Success(c, gin.H{"deliver": update}, "修改成功")
}

func (d DeliverController) Delete(c *gin.Context) {
	// 不传haspaid的删除：作废元祖，归还吨数
	var del model.Deliver // 保存出口件数、吨数
	u, _ := uuid.Parse(c.Params.ByName("uuid"))
	d.DB.Model(&model.Deliver{}).Where("uuid=?", u).First(&del)
	// 归还吨数
	index := len(del.Enterid) - 1
	subDeliver := del.Quality
	for i := 0; i <= index; i++ {
		var returnEnter model.Enter
		uuidE, _ := uuid.Parse(del.Enterid[i])
		d.DB.Model(&model.Enter{}).Where("uuid=?", uuidE).First(&returnEnter)
		sub := returnEnter.Quality - returnEnter.RemainQuality
		if sub > 0 {
			if sub-subDeliver >= 0 {
				d.DB.Model(&model.Enter{}).Where("uuid=?", uuidE).
					UpdateColumn("remain_quality", returnEnter.RemainQuality+subDeliver)
				break
			}
			d.DB.Model(&model.Enter{}).Where("uuid=?", uuidE).
				UpdateColumn("remain_quality", sub)
			subDeliver -= sub
		}
	}
	// 删除出库元祖
	err := d.DB.Model(&model.Deliver{}).Where("uuid=?", u).Delete(&model.Deliver{}).Error
	if err != nil {
		response.Fail(c, err.Error(), nil)
		return
	}
	response.Success(c, nil, "删除成功")
}

func (d DeliverController) PageList(c *gin.Context) {
	id, _ := c.GetQuery("cid")
	if id != "" {
		var selectDeliver []model.Deliver
		// 查询
		err := d.DB.Model(&model.Deliver{}).Where("cid=?", id).Find(&selectDeliver).Error
		if err != nil {
			response.Fail(c, err.Error(), nil)
			return
		}
		response.Success(c, gin.H{"deliver": selectDeliver}, "查询成功")
		return
	}
	// 获取分页参数
	tempNum, _ := c.GetQuery("pagenum")
	tempSize, _ := c.GetQuery("pagesize")
	pageNum, _ := strconv.Atoi(tempNum)   // 当前页数
	pageSize, _ := strconv.Atoi(tempSize) // 页内个数
	if pageNum == 0 {
		pageNum = 1
	}
	if pageSize == 0 {
		pageSize = 10
	}
	// 分页
	var delivers []model.Deliver
	d.DB.Order("ddate ASC").Offset((pageNum - 1) * pageSize).Limit(pageSize).
		Find(&delivers)
	// 前端渲染分页也要知道总数
	var total int64
	d.DB.Model(model.Deliver{}).Count(&total)

	response.Success(c, gin.H{"data": delivers, "total": total}, "查询成功")
}

func (d DeliverController) Visual(c *gin.Context) {
	// 出库表enterUuid-id-堆存费 数组
	// 获取出库表
	var allDelivers []model.Deliver
	err := d.DB.Model(&model.Deliver{}).Find(&allDelivers).Error
	if err != nil {
		response.Fail(c, err.Error(), nil)
	}
	// 数组：公司号-堆存费
	companyFee := make([]model.CompanyFee, 0)
	// 累计各公司的堆存费
	for i := 0; i < len(allDelivers); i++ {
		var ok bool
		if i == 0 {
			companyFee = append(companyFee, model.CompanyFee{
				Id:  allDelivers[i].Cid,
				Fee: allDelivers[i].Fee,
			})
			continue
		}
		flag := 0
		for k := 0; k < len(companyFee); k++ {
			ok = companyFee[k].Id == allDelivers[i].Cid
			if ok == true {
				flag = 1
				companyFee[k].Fee += allDelivers[i].Fee
			}
		}
		if flag == 0 {
			companyFee = append(companyFee, model.CompanyFee{
				Id:  allDelivers[i].Cid,
				Fee: allDelivers[i].Fee,
			})
		}
	}
	response.Success(c, gin.H{"pair": companyFee}, "查询成功")
}
